package com.gaogzhen.utils;

import java.util.LinkedList;

/**
 * 计算器：整数
 *  1、中缀表达式计算
 *      1.1、包含+、-、*、/、(、）
 *      1.2、可以+/-起始
 *      1.3、可以为多位整数
 *      1.4、示例：-20+(1*(3-5)*2-5)/2
 *  2、后缀表达式计算
 *  3、中缀表达式转后缀
 */
public class CalculatorSimple {
    /**
     * 中缀表达式计算
     * @param express   中缀表达式
     * @return          计算结果
     * 步骤
     * 1、定义
     *  1.1、分别存放树栈n和存储运算符栈o
     *  1.2、'+'/'/'运算符优先级10，'+'/'-'运算符优先级5
     *  1.3、 start数字在字符串中起始索引，count同一数字占字符个数，
     *
     * 2、算法
     *  2.1、循环字符串、顺序获取每一个字符
     *  2.2、判断字符是否是数字
     *  2.3、是数字，则count++,继续循环
     *  2.4、不是数字
     *      2.4.1、字符不是（且截取的字符串不为空，把子串转换为整数出入栈n
     *      2.4.2、start后移，count清零
     *      2.4.3、栈o为空或者字符为（，把字符存入栈o
     *      2.4.4、字符为），则计算()中的结果
     *      2.4.5、否则循环判断当前字符与栈o顶部字符优先级
     *          2.4.5.1、前面优先级高，
     *          2.4.5.2、取栈o栈顶运算符
     *          2.4.5.3、栈n连取2个数计算结果且存入栈n
     *      2.4.6、把字符存入栈o
     *  2.5、如果栈o不为空，循环计算结果，过程同2.4.5
     *  2.6、取出栈n栈顶元素即为计算结果
     */
    public static int infix(String express) {
//        String[] split = express.split("[+-*/]");
        LinkedList<Integer> n = new LinkedList<>();
        LinkedList<Character> o = new LinkedList<>();
        int l = express.length();
        int count = 1;
        int start = 0;
        // 起始1 默认第一个字符为第一个数字的第一位，可以为符号位
        for (int i = 1; i < l; i++) {
            char c;
            if (isNum(c = express.charAt(i)))
                count++;
            else {
                String sub = express.substring(start, start + count);
                if (c != '(' && !sub.equals("")) {
                    n.push(Integer.parseInt(sub));
                }
                start += count + 1;
                count = 0;
                if (o.isEmpty() || c == '(')
                    o.push(c);
                else {

                    char t;
                    if (c == ')') {
                        while (!o.isEmpty() && (t = o.pop()) != '(') {
                            Integer n2 = n.pop();
                            Integer n1 = n.pop();
                            n.push(cal(n1, n2, t));
                        }
                    } else {
                        while (!o.isEmpty() && (t = o.peek()) != '(' && priority(c) <= priority(t)) {
                            t = o.pop();
                            Integer n2 = n.pop();
                            Integer n1 = n.pop();

                            n.push(cal(n1, n2, t));
                        }
                        o.push(c);
                    }
                }
            }
        }
        n.push(Integer.parseInt(express.substring(start, start + count)));
        while (!o.isEmpty()) {
            char t = o.pop();
            Integer n2 = n.pop();
            Integer n1 = n.pop();
            n.push(cal(n1, n2, t));
        }
        return n.pop();
    }


    /**
     * 运算符优先级
     * @param o     运算符
     * @return      运算符对应的优先级
     */
    private static int priority(char o) {
        int p;
        if (o == '*' || o == '/')
            p = 10;
        else if (o == '+' || o == '-')
            p = 5;
        else
            p = 0;
        return p;
    }

    /**
     * 判断字符是否是数字
     * @param c     目标字符
     * @return      true是数字;false不是数字
     */
    private static boolean isNum(char c) {
        return c >= '0' && c <= '9';
    }

    /**
     * 计算2个操作数的运算结果
     * @param n1    操作数1
     * @param n2    操作数2
     * @param o     运算符
     * @return      计算结果
     */
    private static int cal(int n1, int n2, char o) {
        int r;
        switch (o) {
            case '+':
                r = n1 + n2;
                break;
            case '-':
                r = n1 - n2;
                break;
            case '*':
                r = n1 * n2;
                break;
            case '/':
                r = n1 / n2;
                break;
            default:
                throw new IllegalStateException("Unexpected value: " + o);
        }
        return r;
    }
}
